/*
 * Copyright (C) 2010, 2012-2013, 2015 ARM Limited. All rights reserved.
 * 
 * This program is free software and is provided to you under the terms of the GNU General Public License version 2
 * as published by the Free Software Foundation, and any use by you of this program is subject to the terms of such GNU licence.
 * 
 * A copy of the licence is included with the program, and can also be obtained from Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#include "drmP.h"
#include "mali_drm.h"
#include "mali_drv.h"

#define VIDEO_TYPE 0
#define MEM_TYPE 1

#define MALI_MM_ALIGN_SHIFT 4
#define MALI_MM_ALIGN_MASK ( (1 << MALI_MM_ALIGN_SHIFT) - 1)


static void *mali_sman_mm_allocate(void *private, unsigned long size, unsigned alignment)
{
	printk(KERN_ERR "DRM: %s\n", __func__);
	return NULL;
}

static void mali_sman_mm_free(void *private, void *ref)
{
	printk(KERN_ERR "DRM: %s\n", __func__);
}

static void mali_sman_mm_destroy(void *private)
{
	printk(KERN_ERR "DRM: %s\n", __func__);
}

static unsigned long mali_sman_mm_offset(void *private, void *ref)
{
	printk(KERN_ERR "DRM: %s\n", __func__);
	return ~((unsigned long)ref);
}

static int mali_fb_init(struct drm_device *dev, void *data, struct drm_file *file_priv)
{
	drm_mali_private_t *dev_priv = dev->dev_private;
	drm_mali_fb_t *fb = data;
	int ret;
	printk(KERN_ERR "DRM: %s\n", __func__);

	mutex_lock(&dev->struct_mutex);
	{
		struct drm_sman_mm sman_mm;
		sman_mm.private = (void *)0xFFFFFFFF;
		sman_mm.allocate = mali_sman_mm_allocate;
		sman_mm.free = mali_sman_mm_free;
		sman_mm.destroy = mali_sman_mm_destroy;
		sman_mm.offset = mali_sman_mm_offset;
		ret = drm_sman_set_manager(&dev_priv->sman, VIDEO_TYPE, &sman_mm);
	}

	if (ret)
	{
		DRM_ERROR("VRAM memory manager initialisation error\n");
		mutex_unlock(&dev->struct_mutex);
		return ret;
	}

	dev_priv->vram_initialized = 1;
	dev_priv->vram_offset = fb->offset;

	mutex_unlock(&dev->struct_mutex);
	DRM_DEBUG("offset = %u, size = %u\n", fb->offset, fb->size);

	return 0;
}

static int mali_drm_alloc(struct drm_device *dev, struct drm_file *file_priv, void *data, int pool)
{
	drm_mali_private_t *dev_priv = dev->dev_private;
	drm_mali_mem_t *mem = data;
	int retval = 0;
	struct drm_memblock_item *item;
	printk(KERN_ERR "DRM: %s\n", __func__);

	mutex_lock(&dev->struct_mutex);

	if (0 == dev_priv->vram_initialized)
	{
		DRM_ERROR("Attempt to allocate from uninitialized memory manager.\n");
		mutex_unlock(&dev->struct_mutex);
		return -EINVAL;
	}

	mem->size = (mem->size + MALI_MM_ALIGN_MASK) >> MALI_MM_ALIGN_SHIFT;
	item = drm_sman_alloc(&dev_priv->sman, pool, mem->size, 0,
	                      (unsigned long)file_priv);

	mutex_unlock(&dev->struct_mutex);

	if (item)
	{
		mem->offset = dev_priv->vram_offset + (item->mm->offset(item->mm, item->mm_info) << MALI_MM_ALIGN_SHIFT);
		mem->free = item->user_hash.key;
		mem->size = mem->size << MALI_MM_ALIGN_SHIFT;
	}
	else
	{
		mem->offset = 0;
		mem->size = 0;
		mem->free = 0;
		retval = -ENOMEM;
	}

	DRM_DEBUG("alloc %d, size = %d, offset = %d\n", pool, mem->size, mem->offset);

	return retval;
}

static int mali_drm_free(struct drm_device *dev, void *data, struct drm_file *file_priv)
{
	drm_mali_private_t *dev_priv = dev->dev_private;
	drm_mali_mem_t *mem = data;
	int ret;
	printk(KERN_ERR "DRM: %s\n", __func__);

	mutex_lock(&dev->struct_mutex);
	ret = drm_sman_free_key(&dev_priv->sman, mem->free);
	mutex_unlock(&dev->struct_mutex);
	DRM_DEBUG("free = 0x%lx\n", mem->free);

	return ret;
}

static int mali_fb_alloc(struct drm_device *dev, void *data, struct drm_file *file_priv)
{
	printk(KERN_ERR "DRM: %s\n", __func__);
	return mali_drm_alloc(dev, file_priv, data, VIDEO_TYPE);
}

static int mali_ioctl_mem_init(struct drm_device *dev, void *data, struct drm_file *file_priv)
{
	drm_mali_private_t *dev_priv = dev->dev_private;
	drm_mali_mem_t *mem = data;
	int ret;
	dev_priv = dev->dev_private;
	printk(KERN_ERR "DRM: %s\n", __func__);

	mutex_lock(&dev->struct_mutex);
	ret = drm_sman_set_range(&dev_priv->sman, MEM_TYPE, 0, mem->size >> MALI_MM_ALIGN_SHIFT);

	if (ret)
	{
		DRM_ERROR("MEM memory manager initialisation error\n");
		mutex_unlock(&dev->struct_mutex);
		return ret;
	}

	mutex_unlock(&dev->struct_mutex);

	return 0;
}

static int mali_ioctl_mem_alloc(struct drm_device *dev, void *data,
                                struct drm_file *file_priv)
{

	printk(KERN_ERR "DRM: %s\n", __func__);
	return mali_drm_alloc(dev, file_priv, data, MEM_TYPE);
}

static drm_local_map_t *mem_reg_init(struct drm_device *dev)
{
	struct drm_map_list *entry;
	drm_local_map_t *map;
	printk(KERN_ERR "DRM: %s\n", __func__);

	list_for_each_entry(entry, &dev->maplist, head)
	{
		map = entry->map;

		if (!map)
		{
			continue;
		}

		if (map->type == _DRM_REGISTERS)
		{
			return map;
		}
	}
	return NULL;
}

int mali_idle(struct drm_device *dev)
{
	drm_mali_private_t *dev_priv = dev->dev_private;
	uint32_t idle_reg;
	unsigned long end;
	int i;
	printk(KERN_ERR "DRM: %s\n", __func__);

	if (dev_priv->idle_fault)
	{
		return 0;
	}

	return 0;
}


void mali_lastclose(struct drm_device *dev)
{
	drm_mali_private_t *dev_priv = dev->dev_private;
	printk(KERN_ERR "DRM: %s\n", __func__);

	if (!dev_priv)
	{
		return;
	}

	mutex_lock(&dev->struct_mutex);
	drm_sman_cleanup(&dev_priv->sman);
	dev_priv->vram_initialized = 0;
	dev_priv->mmio = NULL;
	mutex_unlock(&dev->struct_mutex);
}

void mali_reclaim_buffers_locked(struct drm_device *dev, struct drm_file *file_priv)
{
	drm_mali_private_t *dev_priv = dev->dev_private;
	printk(KERN_ERR "DRM: %s\n", __func__);

	mutex_lock(&dev->struct_mutex);

	if (drm_sman_owner_clean(&dev_priv->sman, (unsigned long)file_priv))
	{
		mutex_unlock(&dev->struct_mutex);
		return;
	}

	if (dev->driver->dma_quiescent)
	{
		dev->driver->dma_quiescent(dev);
	}

	drm_sman_owner_cleanup(&dev_priv->sman, (unsigned long)file_priv);
	mutex_unlock(&dev->struct_mutex);
	return;
}

struct drm_ioctl_desc mali_ioctls[] =
{
	DRM_IOCTL_DEF(DRM_MALI_FB_ALLOC, mali_fb_alloc, DRM_AUTH),
	DRM_IOCTL_DEF(DRM_MALI_FB_FREE, mali_drm_free, DRM_AUTH),
	DRM_IOCTL_DEF(DRM_MALI_MEM_INIT, mali_ioctl_mem_init, DRM_AUTH | DRM_MASTER | DRM_ROOT_ONLY),
	DRM_IOCTL_DEF(DRM_MALI_MEM_ALLOC, mali_ioctl_mem_alloc, DRM_AUTH),
	DRM_IOCTL_DEF(DRM_MALI_MEM_FREE, mali_drm_free, DRM_AUTH),
	DRM_IOCTL_DEF(DRM_MALI_FB_INIT, mali_fb_init, DRM_AUTH | DRM_MASTER | DRM_ROOT_ONLY),
};

int mali_max_ioctl = DRM_ARRAY_SIZE(mali_ioctls);
